#include"connectionpool.h"
#include"public.h"


//线程安全的懒汉单例模式
ConnectionPool *ConnectionPool::getConnectionPool()
{
    static ConnectionPool pool;//静态局部变量初始化由编译器自动进行lock和unlock
    return &pool;
} 

bool ConnectionPool::loadConfigFile()//从配置文件加载配置项
{
    FILE *pf = fopen(MYSQL_CONFIG_PATH, "r");
    if(pf==nullptr)
    {
        LOG("配置文件不存在!");
        return false;
    }
    while(!feof(pf))
    {
        char line[BUFF_LEN] = {0};
        fgets(line,BUFF_LEN,pf);
        string str = line;
        int idx = str.find('=', 0);
        if(idx==-1)//无效配置项
        {
            continue;
        }

        // password=111111\n
        int endidx = str.find('\n', idx);
        string key=str.substr(0,idx);
        string value = str.substr(idx + 1, endidx - idx - 1);
        if(key=="ip")
        {
            _ip = value;
        }
        else if(key=="port")
        {
            _port = atoi(value.c_str());
        }
        else if(key=="dbName")
        {
            _dbname = value;
        }
        else if(key=="userName")
        {
            _username = value;
        }
        else if(key=="password")
        {
            _password = value;
        }
        else if(key=="initSize")
        {
            _initSize = atoi(value.c_str());
        }
        else if(key=="maxSize")
        {
            _maxSize = atoi(value.c_str());
        }
        else if(key=="maxIdleTime")
        {
            _maxIdleTime = atoi(value.c_str());
        }
        else if(key=="maxconnectionTimeOut")
        {
            _connectionTimeout = atoi(value.c_str());
        }

    }
    return true;
}

ConnectionPool::ConnectionPool()
{
    //加载配置项
    if(!loadConfigFile())
    {
        return;
    }
    //创建初始连接
    for (int i = 0; i < _initSize;i++)
    {
        Connection *p = new Connection();
        p->connect(_ip, _port, _username, _password, _dbname);
        p->refreshAliveTime();//刷新开始空闲的起始时间
        _connectionque.push(p);
        _conCount++;
    }
    
    //启动一个新的线程，作为连接生产者Linux thread -》pthread_create
    thread produce(bind(&ConnectionPool::produceConnectionTask, this));
    //使用绑定器给成员方法绑定一个当前对象，要不然这个常用方法不能直接作为一个线程函数，因为线程函数都是C接口，C++方法是需要对象的
    produce.detach();

    // 启动一个新的定时线程，扫描超过maxIdleTime时间的空闲连接，进行对于的连接回收
    thread scanner(bind(&ConnectionPool::scannerConnectionTask, this));
    scanner.detach();
}

 void ConnectionPool::produceConnectionTask()//将线程函数写成类的成员方法最大的好处就是可以方便的访问类的成员变量
 { 
    for (;;)
    {
        unique_lock<mutex> lock(_queMutex);
        while(!_connectionque.empty())
        {
            cv.wait(lock);//队列不空，生产线程进入等待状态
        }

        if(_conCount < _maxSize)
        {
            Connection *p = new Connection();
            p->connect(_ip, _port, _username, _password, _dbname);
            p->refreshAliveTime();//刷新开始空闲的起始时间
            _connectionque.push(p);
            _conCount++;
        }

        //通知消费者线程可以消费连接了
        cv.notify_all();
    }
 }
 
//给外部提供接口，从连接池中获取一个可用的空闲连接：消费者线程
shared_ptr<Connection> ConnectionPool::getConnection()
{
    unique_lock<mutex>lock(_queMutex);
    while(_connectionque.empty())
    {
        if(cv_status::timeout==cv.wait_for(lock, chrono::milliseconds(_connectionTimeout)))
        {//超时醒来发现队列还是空的，表明没有连接
            if(_connectionque.empty())
            {
                LOG("获取连接失败");
                return nullptr;
            }
        }
    }
    /*
    shared_ptr智能指针析构时，会把connection资源直接delete掉，
    相当于调用connection的析构函数，connection就被close掉了。
    这里需要自定义shared-ptr的释放资源的方式，把connection直接归还到queue当中
    */
    shared_ptr<Connection> sp(_connectionque.front(),
                              [&](Connection *pcon)
                              {
                                unique_lock<mutex> lock(_queMutex);//这里是在服务器应用线程中调用的，所以一定要考虑队列的线程安全操作
                                pcon->refreshAliveTime();//刷新开始空闲的起始时间
                                _connectionque.push(pcon);
                              });

    _connectionque.pop();
    if(_connectionque.empty())
    {
        cv.notify_all();//谁消费了队列里面最后一个连接，谁负责通知生产者线程进行生产
    }
    
    return sp;
}

void ConnectionPool::scannerConnectionTask()
{
    for (;;)
    {
        //通过sleep模拟定时效果
        this_thread::sleep_for(chrono::seconds(_maxIdleTime));

        //扫描整个队列释放多余的连接
        unique_lock<mutex>lock(_queMutex);
        while(_conCount>_initSize)
        {
            Connection *p = _connectionque.front();
            if(p->getAliveTime()>=(_maxIdleTime*1000))
            {
                _connectionque.pop();
                _conCount -- ;
                delete p; // 调用~Connection()释放连接
            }
            else//对头都没有超过时间，后面的连接肯定也没有超过
            {
                break;
            }
        }
    }
}